package cn.goour.web.sys.controller


import cn.goour.web.Initialized
import cn.goour.web.base.tools.getDeviceBean
import cn.goour.web.sys.entity.LoginApiKey
import cn.goour.web.sys.entity.User
import cn.goour.web.sys.entity.type.LoginType
import cn.goour.web.sys.repository.LoginApiKeyRepository
import cn.goour.web.sys.service.UserLoginService
import cn.yiban.util.HTTPSimple
import com.alibaba.fastjson.JSONObject
import com.qq.connect.QQConnectException
import com.qq.connect.api.OpenID
import com.qq.connect.api.qzone.PageFans
import com.qq.connect.api.qzone.Topic
import com.qq.connect.api.qzone.UserInfo
import com.qq.connect.utils.QQConnectConfig
import getAttributeToBoolean
import keyToI18NValue
import org.apache.commons.lang.RandomStringUtils
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.ModelMap
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import setUser
import weibo4j.model.WeiboException
import weibo4j.util.WeiboConfig
import java.io.UnsupportedEncodingException
import java.net.URLEncoder
import java.util.*
import javax.annotation.PostConstruct
import javax.security.auth.login.LoginException
import javax.servlet.http.HttpServletRequest

@Controller
@RequestMapping("/login")
class UserLoginController {

    private var slPrefix: String = Initialized.slPrefix

    private val loginErrorMsgKey = "loginErrorMsg"

    private val weiBoOauth = weibo4j.Oauth()
    private val qqOauth = com.qq.connect.oauth.Oauth()
    private val yiBanAuthorize = cn.yiban.open.Authorize()

    @Autowired
    private lateinit var loginService: UserLoginService
    @Autowired
    private lateinit var loginApiKeyRepository: LoginApiKeyRepository

    init {
        logger.info("initialized ...")
    }

    @PostConstruct
    fun run() {
        logger.info("PostConstruct ...")
    }

    @RequestMapping("/qqLogin")
    fun qqLogin(request: HttpServletRequest): String {
        val url = try {
            val key = getLoginApiKey(LoginType.QQ)
            QQConnectConfig.updateProperties("app_ID", key.appId)
            QQConnectConfig.updateProperties("app_KEY", key.appSecret)
            QQConnectConfig.updateProperties("redirect_URI", key.redirectUri)
            QQConnectConfig.updateProperties("scope", key.scope)
            "redirect:${qqOauth.getAuthorizeURL(request)}"
        } catch (e: Exception) {
            e.printStackTrace()
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }
        logger.info(url)
        return url
    }

    @RequestMapping(value = "/qqLogin", params = arrayOf("code", "state"))
    fun qqLogin(state: String, request: HttpServletRequest): String {
        val url = try {
            val theAttrKey = slPrefix + LoginType.QQ
            when {
                request.session.isNew || !request.servletContext.getAttributeToBoolean(theAttrKey) ->
                    "redirect:qqLogin"
                request.session.getAttributeToBoolean(theAttrKey) -> // 判断当前会话的登录状态，当QQ登陆状态已经被标识时，不再执行登录操作，直接返回
                    "redirect:success"
                state == request.session.getAttribute("qq_connect_state") -> {// 校验state参数是否正确
                    val token = qqOauth.getAccessTokenByRequest(request)

                    if (!token.accessToken.isNullOrEmpty()) {
                        // 当无法获取到QQ登陆条目时，并且在会话中有authorize参数时表明当前会话已经登录，系统自动为已登录的用户添加QQ登陆方式
                        val user = loginService.getLogin(token)
                        request.session.setAttribute(theAttrKey, true)
                        request.setUser(user)
                        logger.info("登录的用户：{}", user)
                        logger.info("登录的用户登录方式：{}", user.authorizes)
                        "redirect:success"
                    } else {
                        throw Exception("login.error.token".keyToI18NValue())
                    }
                }
                else -> // state参数校验失败，这是一个非法的登录返回请求
                    throw Exception("login.error.state".keyToI18NValue())
            }
        } catch (e: LoginException) {
            logger.info("这是Service层已知的登录出现错误：${e.message}")
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        } catch (e: Exception) {
            e.printStackTrace()
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @RequestMapping("/qqInfo")
    fun getQQInfo(request: HttpServletRequest): String? {
        val session = request.session
        try {
            val user = session.getAttribute("user") as User
            logger.info("登录的用户：{}", user)
            logger.info("登录的用户登录方式：{}", user.authorizes)
            val authorize = user.getUserLoginAuthorizeByLoginType(LoginType.QQ)
            logger.info("QQ登录：{}", authorize)
            if (authorize?.credential == "") {
                //                我们的网站被CSRF攻击了或者用户取消了授权
                //                做一些数据统计工作
                print("没有获取到响应参数")
            } else {
                val accessToken = authorize?.credential ?: ""
                logger.info("获得的Token：{}", accessToken)

                val openID: String

                // 利用获取到的accessToken 去获取当前用的openid -------- start
                val openIDObj = OpenID(accessToken)
                openID = openIDObj.userOpenID

                logger.info("欢迎你，代号为 $openID 的用户!")
                request.session.setAttribute("demo_openid", openID)
                val con = "发表说说内容，本条说说为测试内容。\n发布时间：${Date()}"

                System.out.println(accessToken)
                System.out.println(openID)
                //请开发者自行校验获取的con值是否有效
                if (con != "") {
                    val topic = Topic(accessToken, openID)
                    try {
                        val grb = topic.addTopic(con)
                        if (grb.ret == 0) {
                            logger.info("您的说说已发表成功，请登录Qzone查看")
                        } else {
                            logger.info("很遗憾的通知您，发表说说失败！原因： " + grb.msg)
                        }
                    } catch (e: QQConnectException) {
                        System.out.println("抛异常了？")
                    }
                } else {
                    System.out.println("获取到的值为空？")
                }


                // 利用获取到的accessToken 去获取当前用户的openid --------- end


                logger.info("start -----------------------------------利用获取到的accessToken,openid 去获取用户在Qzone的昵称等信息 ---------------------------- start")
                val qzoneUserInfo = UserInfo(accessToken, openID)
                val userInfoBean = qzoneUserInfo.userInfo
                logger.info("<br/>")
                if (userInfoBean.ret == 0) {
                    logger.info(userInfoBean.nickname)
                    logger.info(userInfoBean.gender)
                    logger.info("黄钻等级： " + userInfoBean.level)
                    logger.info("会员 : " + userInfoBean.isVip)
                    logger.info("黄钻会员： " + userInfoBean.isYellowYearVip)
                    logger.info("<image src=" + userInfoBean.avatar.avatarURL30)
                    logger.info("<image src=" + userInfoBean.avatar.avatarURL50)
                    logger.info("<image src=" + userInfoBean.avatar.avatarURL100)
                } else {
                    logger.info("很抱歉，我们没能正确获取到您的信息，原因是： " + userInfoBean.msg)
                }
                logger.info("end -----------------------------------利用获取到的accessToken,openid 去获取用户在Qzone的昵称等信息 ---------------------------- end")
                logger.info("start ----------------------------------- 验证当前用户是否为认证空间的粉丝------------------------------------------------ start")
                val pageFansObj = PageFans(accessToken, openID)
                val pageFansBean = pageFansObj.checkPageFans("97700000")
                if (pageFansBean.ret == 0) {
                    logger.info("验证您" + (if (pageFansBean.isFans) "是" else "不是") + "QQ空间97700000官方认证空间的粉丝")
                } else {
                    logger.info("很抱歉，我们没能正确获取到您的信息，原因是： " + pageFansBean.msg)
                }
                logger.info("end ----------------------------------- 验证当前用户是否为认证空间的粉丝------------------------------------------------ end")
                logger.info("start -----------------------------------利用获取到的accessToken,openid 去获取用户在微博的昵称等信息 ---------------------------- start")
                val weiboUserInfo = com.qq.connect.api.weibo.UserInfo(accessToken, openID)
                val weiboUserInfoBean = weiboUserInfo.userInfo
                if (weiboUserInfoBean.ret == 0) {
                    //获取用户的微博头像----------------------start
                    logger.info("<image src=" + weiboUserInfoBean.avatar.avatarURL30 + "/>")
                    logger.info("<image src=" + weiboUserInfoBean.avatar.avatarURL50 + "/>")
                    logger.info("<image src=" + weiboUserInfoBean.avatar.avatarURL100 + "/>")
                    //获取用户的微博头像 ---------------------end
                    //获取用户的生日信息 --------------------start
                    logger.info("尊敬的用户，你的生日是： " + weiboUserInfoBean.birthday.year
                            + "年" + weiboUserInfoBean.birthday.month + "月" +
                            weiboUserInfoBean.birthday.day + "日")
                    //获取用户的生日信息 --------------------end
                    val sb = StringBuffer()
                    sb.append("所在地:" + weiboUserInfoBean.countryCode + "-" + weiboUserInfoBean.provinceCode + "-" + weiboUserInfoBean.cityCode
                            + weiboUserInfoBean.location)

                    //获取用户的公司信息---------------------------start
                    val companies = weiboUserInfoBean.companies
                    if (companies.size > 0) {
                        //有公司信息
                        var i = 0
                        val j = companies.size
                        while (i < j) {
                            sb.append("曾服役过的公司：公司ID-" + companies[i].id + " 名称-" +
                                    companies[i].companyName + " 部门名称-" + companies[i].departmentName + " 开始工作年-" +
                                    companies[i].beginYear + " 结束工作年-" + companies[i].endYear)
                            i++
                        }
                    } else {
                        //没有公司信息
                    }
                    //获取用户的公司信息---------------------------end
                    logger.info(sb.toString())
                } else {
                    logger.info("很抱歉，我们没能正确获取到您的信息，原因是： " + weiboUserInfoBean.msg)
                }
                logger.info("end -----------------------------------利用获取到的accessToken,openid 去获取用户在微博的昵称等信息 ---------------------------- end")
            }
        } catch (e: QQConnectException) {
            e.printStackTrace()
        }

        return "index"
    }

    @RequestMapping("/weiBoLogin")
    fun weiBoLogin(request: HttpServletRequest): String {
        val url = try {
            val key = getLoginApiKey(LoginType.WeiBo)
            WeiboConfig.updateProperties("client_ID", key.appId)
            WeiboConfig.updateProperties("client_SERCRET", key.appSecret)
            WeiboConfig.updateProperties("redirect_URI", key.redirectUri)
            WeiboConfig.updateProperties("scope", key.scope)

            val state = RandomStringUtils.randomAlphanumeric(6)
            request.session.setAttribute("state", state)

            "redirect:${weiBoOauth.authorize("code", state, key.scope)}"
        } catch (e: Exception) {
            e.printStackTrace()
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }
        logger.info(url)
        return url
    }

    @RequestMapping(value = "/weiBoLogin", params = arrayOf("code", "state"))
    fun weiBoLogin(code: String, state: String, request: HttpServletRequest): String {
        val url = try {
            val theAttrKey = slPrefix + LoginType.WeiBo
            when {
                request.session.isNew || !request.servletContext.getAttributeToBoolean(theAttrKey) ->
                    "redirect:weiBoLogin"
                request.session.getAttributeToBoolean(theAttrKey) -> // 判断当前会话的登录状态，当微博登陆状态已经被标识时，不再执行登录操作，直接返回
                    "redirect:success"
                state == request.session.getAttribute("state") -> { // 校验state参数是否正确
                    val token = weiBoOauth.getAccessTokenByCode(code)
                    if (!token.accessToken.isNullOrEmpty()) {
                        // 当无法获取到微博登陆条目时，并且在会话中有authorize参数时表明当前会话已经登录，系统自动为已登录的用户添加微博登陆方式
                        val user = loginService.getLogin(token)
                        request.session.setAttribute(theAttrKey, true)
                        request.setUser(user)
                        "redirect:success"
                    } else {
                        throw Exception("login.error.token".keyToI18NValue())
                    }
                }
                else -> // state参数校验失败，这是一个非法的登录返回请求
                    throw Exception("login.error.state".keyToI18NValue())
            }
        } catch (e: WeiboException) {
            e.printStackTrace()
            val message = if (401 == e.statusCode) {
                "无法获得第三方会话Token"
            } else {
                e.error
            }
            request.session.setAttribute(loginErrorMsgKey, "错误：$message")
            "redirect:error"
        } catch (e: LoginException) {
            logger.info("这是Service层已知的登录出现错误：${e.message}")
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        } catch (e: Exception) {
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @RequestMapping("/yiBanLogin")
    fun yiBanLogin(request: HttpServletRequest): String {
        val url = try {
            val key = getLoginApiKey(LoginType.YiBan)
            val state = RandomStringUtils.randomAlphanumeric(6)
            request.session.setAttribute("state", state)
            yiBanAuthorize.appKey = key.appId
            yiBanAuthorize.appSecret = key.appSecret
            yiBanAuthorize.redirectUri = key.redirectUri

            "redirect:${yiBanAuthorize.forwardurl(state, cn.yiban.open.Authorize.DISPLAY_TAG_T.WEB)}"
        } catch (e: Exception) {
            e.printStackTrace()
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @RequestMapping("/yiBanLogin", params = arrayOf("code", "state"))
    fun yiBanLogin(code: String, state: String, request: HttpServletRequest): String {
        val url = try {
            val theAttrKey = slPrefix + LoginType.YiBan
            when {
                request.session.isNew || !request.servletContext.getAttributeToBoolean(theAttrKey) -> {
                    "redirect:yiBanLogin"
                }
                request.session.getAttributeToBoolean(theAttrKey) -> // 判断当前会话的登录状态，当微博登陆状态已经被标识时，不再执行登录操作，直接返回
                    "redirect:success"
                state == request.session.getAttribute("state") -> {// 校验state参数是否正确
                    val jsonStr = yiBanAuthorize.querytoken(code)

                    val token = cn.yiban.open.common.entity.AccessToken.parseJsonObject(JSONObject.parseObject(jsonStr))
                    if (!token.access_token.isNullOrEmpty()) {
                        // 当无法获取到微博登陆条目时，并且在会话中有authorize参数时表明当前会话已经登录，系统自动为已登录的用户添加微博登陆方式
                        val user = loginService.getLogin(token)
                        request.session.setAttribute(theAttrKey, true)
                        request.setUser(user)
                        "redirect:success"
                    } else {
                        throw Exception("login.error.token".keyToI18NValue() + "，${token.accessTokenInfo.msgCN}")
                    }
                }
                else -> // state参数校验失败，这是一个非法的登录返回请求
                    throw Exception("login.error.state".keyToI18NValue())
            }
        } catch (e: LoginException) {
            logger.info("这是Service层已知的登录出现错误：${e.message}")
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        } catch (e: Exception) {
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @RequestMapping("/weiXinLogin")
    fun weXinLogin(request: HttpServletRequest): String {
        val device = request.getDeviceBean()
        val url = try {
            // 默认设置是网站方式登录
            val isMobile = device.is_mobile || device.is_phone
            if (isMobile && device.advertised_app_name == "WeChat") {
                // 这是一个微信客户端访问
                weXinLoginMobile(request)
            } else {
                // 这是一个网页版的访问，有可能是手机浏览器也有可能是电脑浏览器
                weXinLoginWeb(request)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @Throws(Exception::class)
    private fun weXinLoginWeb(request: HttpServletRequest): String {
        val host = "https://open.weixin.qq.com/"
        val key = getLoginApiKey(LoginType.WeiXinWeb)
        val state = RandomStringUtils.randomAlphanumeric(6)

        return "redirect:${getWeiXinLoginApiUrl(request, host, "connect/qrconnect", state, key)}"
    }

    @Throws(Exception::class)
    private fun weXinLoginMobile(request: HttpServletRequest): String {
        val host = "https://open.weixin.qq.com/"
        val key = getLoginApiKey(LoginType.WeiXin)
        val state = RandomStringUtils.randomAlphanumeric(6)

        return "redirect:${getWeiXinLoginApiUrl(request, host, "connect/oauth2/authorize", state, key)}"
    }

    @RequestMapping("/weiXinLogin", params = arrayOf("code", "state"))
    fun weChatLogin(code: String, state: String, request: HttpServletRequest): String {
        val url = try {
            val theAttrKey = slPrefix + LoginType.WeiXin
            when {
                request.session.isNew || (!request.servletContext.getAttributeToBoolean(theAttrKey) && !request.servletContext.getAttributeToBoolean(slPrefix + LoginType.WeiXinWeb)) ->
                    "redirect:weiXinLogin"
                request.session.getAttributeToBoolean(theAttrKey) -> // 判断当前会话的登录状态，当微博登陆状态已经被标识时，不再执行登录操作，直接返回
                    "redirect:success"
                state == request.session.getAttribute("state") -> { // 校验state参数是否正确
                    val url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=${request.session.getAttribute("weixin.appid")}&secret=${request.session.getAttribute("weixin.appkey")}&code=$code&grant_type=authorization_code"
                    val jsonStr = HTTPSimple.GET(url)

                    val token = com.qq.weixin.open.entity.AccessToken.parseJsonObject(JSONObject.parseObject(jsonStr))
                    if (!token.access_token.isNullOrEmpty()) {
                        // 当无法获取到微博登陆条目时，并且在会话中有authorize参数时表明当前会话已经登录，系统自动为已登录的用户添加微博登陆方式
                        val user = loginService.getLogin(token)
                        request.session.setAttribute(theAttrKey, true)
                        request.setUser(user)
                        "redirect:success"
                    } else {
                        throw Exception("login.error.token".keyToI18NValue() + "，${token.errmsg}")
                    }
                }
                else -> // state参数校验失败，这是一个非法的登录返回请求
                    throw Exception("login.error.state".keyToI18NValue())
            }
        } catch (e: LoginException) {
            logger.info("这是Service层已知的登录出现错误：${e.message}")
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        } catch (e: Exception) {
            request.session.setAttribute(loginErrorMsgKey, "错误：${e.message}")
            "redirect:error"
        }

        logger.info(url)
        return url
    }

    @RequestMapping("/success")
    fun success(): String {
        logger.info("loginSuccess")

        return "userLogin/loginSuccess"
    }

    @RequestMapping("/error")
    fun error(): String {
        logger.info("loginError")

        return "userLogin/loginError"
    }

    @RequestMapping("/localLogin")
    fun localLogin(map: ModelMap): String {
        logger.info("localLogin run")

        map.addAttribute("text", "这是一个属性")

        return "index"
    }

    private fun getWeiXinLoginApiUrl(request: HttpServletRequest, host: String, uri: String, state: String, key: LoginApiKey): String {
        val redirectUrl = try {
            URLEncoder.encode(key.redirectUri, "utf-8")
        } catch (e: UnsupportedEncodingException) {
            key.redirectUri
        }
        request.session.setAttribute("weixin.appid", key.appId)
        request.session.setAttribute("weixin.appkey", key.appSecret)
        request.session.setAttribute("weixin.redirectUri", key.redirectUri)
        request.session.setAttribute("weixin.scope", key.scope)
        request.session.setAttribute("state", state)
        val url = "$host$uri?appid=${key.appId}&redirect_uri=$redirectUrl&response_type=code&scope=${key.scope}&state=$state#wechat_redirect"
        println(url)
        return url
    }

    @Throws(Exception::class)
    private fun getLoginApiKey(loginType: LoginType): LoginApiKey {
        val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes).request
        val errorMsg = if (request.servletContext.getAttributeToBoolean(slPrefix + loginType)) {// 并且当前登录方式处于开启状态
            val key: LoginApiKey? = loginApiKeyRepository.getByLoginType(loginType)
            if (key != null) {
                return key
            } else {
                // 没有微博登陆的API信息
                "login.api.not-setting".keyToI18NValue().format(loginType.text)
            }
        } else {
            // 当前登录方式未开启
            "login.api.not-open".keyToI18NValue().format(loginType.text)
        }
        throw Exception(errorMsg)
    }

    companion object {
        private val logger = LoggerFactory.getLogger(UserLoginController::class.java)
    }

}